Intro to wind energy

Exercise 1

Map wind turbines in Rogaland from OSM and calculate the total energy capacity installed.

Objetives

  • OSM (and how get data from it)
  • Map points data (interactive map)
  • Manipulate spatial data (e.g., intersection points - polygons)
  • Types of data in a data frame
  • Data wrangling

Solution

Load libraries

Show the code
library(osmdata)
library(sf)
library(tmap)
library(tidyverse)

Get data from OpenStreetMap with osmdata, and transform to a sf object.

Show the code
query <- opq(bbox = "Rogaland", timeout = 50) |> 
   add_osm_feature(key = "generator:source", value = "wind")
data <-  osmdata_sf(query)

wind_turbines <- data$osm_points 

Names of the data frame,

Show the code
wind_turbines |>
  names()
 [1] "osm_id"                           "name"                            
 [3] "aeroway:light"                    "aeroway:light:character"         
 [5] "aeroway:light:colour"             "aeroway:light:icao_type"         
 [7] "aeroway:light:intensity"          "brand"                           
 [9] "date_constructed"                 "description"                     
[11] "ele"                              "generator:method"                
[13] "generator:output:electricity"     "generator:source"                
[15] "generator:type"                   "height"                          
[17] "man_made"                         "model"                           
[19] "operator"                         "power"                           
[21] "ref"                              "ref:hinder"                      
[23] "ref:kystverket"                   "seamark:landmark:category"       
[25] "seamark:landmark:conspicuity"     "seamark:landmark:vertical_length"
[27] "seamark:light:1:character"        "seamark:light:1:colour"          
[29] "seamark:light:1:group"            "seamark:light:1:height"          
[31] "seamark:light:1:multiple"         "seamark:light:1:period"          
[33] "seamark:light:1:range"            "seamark:light:2:category"        
[35] "seamark:light:2:character"        "seamark:light:2:colour"          
[37] "seamark:name"                     "seamark:type"                    
[39] "serial_number"                    "source"                          
[41] "geometry"                        

Clean names

Show the code
wind_turbines <- wind_turbines |>
  janitor::clean_names()

names(wind_turbines)
 [1] "osm_id"                           "name"                            
 [3] "aeroway_light"                    "aeroway_light_character"         
 [5] "aeroway_light_colour"             "aeroway_light_icao_type"         
 [7] "aeroway_light_intensity"          "brand"                           
 [9] "date_constructed"                 "description"                     
[11] "ele"                              "generator_method"                
[13] "generator_output_electricity"     "generator_source"                
[15] "generator_type"                   "height"                          
[17] "man_made"                         "model"                           
[19] "operator"                         "power"                           
[21] "ref"                              "ref_hinder"                      
[23] "ref_kystverket"                   "seamark_landmark_category"       
[25] "seamark_landmark_conspicuity"     "seamark_landmark_vertical_length"
[27] "seamark_light_1_character"        "seamark_light_1_colour"          
[29] "seamark_light_1_group"            "seamark_light_1_height"          
[31] "seamark_light_1_multiple"         "seamark_light_1_period"          
[33] "seamark_light_1_range"            "seamark_light_2_category"        
[35] "seamark_light_2_character"        "seamark_light_2_colour"          
[37] "seamark_name"                     "seamark_type"                    
[39] "serial_number"                    "source"                          
[41] "geometry"                        

Interactive map with tmap.

Show the code
tmap_mode("view")

tm_shape(wind_turbines) +
  tm_dots(col = "#0072B2")
Figure 1: Wind turbines in Rogaland (Data from OSM)

There are some wind turbines that ar not in Rogaland, so we are going to delect them from the dataset. For that we need to intersect our points (wind turbines) with the polygon (Rogaland).

Show the code
# Get Norwegian counties (polygons)) from GISCO
counties <- giscoR::gisco_get_nuts(country = "NO",
                                   year = "2021",
                                   nuts_level = 3,
                                   epsg = "4326",
                                   resolution = "01") 
# Get only rogaland county
rogaland <- counties |> 
  filter(NUTS_NAME == "Rogaland")

# Intersect wind turbines (points) with rogaland (polygons)
wind_turbines <- wind_turbines |> 
  st_intersection(rogaland)

# Plot
tm_shape(wind_turbines) +
  tm_dots(col = "#0072B2")
Figure 2: Wind turbines in Rogaland (Data from OSM)

Now, we are going to calculate the maximum capacity (MW) installed in the region. If we inspect the data, there is a column describing called generator:output:electricity, which give the information we are looking for in MW. However, if we inspect the columns we see that the values are characters (<chr>) not numbers, so we can not carried out numerical operations on them.

Show the code
glimpse(wind_turbines)
Rows: 256
Columns: 51
$ osm_id                           <chr> "1277731149", "1510248163", "15102482…
$ name                             <chr> "Høg-Jæren energipark 31", "Høg-Jæren…
$ aeroway_light                    <chr> "obstacle", NA, "obstacle", "obstacle…
$ aeroway_light_character          <chr> "flashing", NA, "flashing", "flashing…
$ aeroway_light_colour             <chr> "red", NA, "red", "red", "red", "red"…
$ aeroway_light_icao_type          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ aeroway_light_intensity          <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ brand                            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ date_constructed                 <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ description                      <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ele                              <chr> "264", "268", "250", "234", "205", "2…
$ generator_method                 <chr> "wind_turbine", "wind_turbine", "wind…
$ generator_output_electricity     <chr> "2.3 MW", "2.3 MW", "2.3 MW", "2.3 MW…
$ generator_source                 <chr> "wind", "wind", "wind", "wind", "wind…
$ generator_type                   <chr> "horizontal_axis", "horizontal_axis",…
$ height                           <chr> "126", "126", "126", "126", "89", "89…
$ man_made                         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ model                            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ operator                         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ power                            <chr> "generator", "generator", "generator"…
$ ref                              <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ref_hinder                       <chr> "70647", "70646", "70640", "71472", "…
$ ref_kystverket                   <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_landmark_category        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_landmark_conspicuity     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_landmark_vertical_length <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_1_character        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_1_colour           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_1_group            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_1_height           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_1_multiple         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_1_period           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_1_range            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_2_category         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_2_character        <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_light_2_colour           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_name                     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ seamark_type                     <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ serial_number                    <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ source                           <chr> "Kartverket Luftfartshindre", "Kartve…
$ NUTS_ID                          <chr> "NO0A1", "NO0A1", "NO0A1", "NO0A1", "…
$ LEVL_CODE                        <dbl> 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3…
$ URBN_TYPE                        <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2…
$ CNTR_CODE                        <chr> "NO", "NO", "NO", "NO", "NO", "NO", "…
$ NAME_LATN                        <chr> "Rogaland", "Rogaland", "Rogaland", "…
$ NUTS_NAME                        <chr> "Rogaland", "Rogaland", "Rogaland", "…
$ MOUNT_TYPE                       <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2…
$ COAST_TYPE                       <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ FID                              <chr> "NO0A1", "NO0A1", "NO0A1", "NO0A1", "…
$ geo                              <chr> "NO0A1", "NO0A1", "NO0A1", "NO0A1", "…
$ geometry                         <POINT [°]> POINT (5.792002 58.65093), POIN…

Therefore, we need to transform the data to numbers. We can use the function parse_number() from the package readr (inside tidyverse). For not deleting the column, we can generate a new one (e.g., gen_electricity_mw)

Show the code
wind_turbines <- wind_turbines |> 
  mutate(gen_electricity_mw = parse_number(generator_output_electricity) )
     
# Show first 10 values of the column
wind_turbines$gen_electricity_mw |> 
  head(10)
 [1] 2.3 2.3 2.3 2.3  NA  NA 2.3 2.3 2.3 2.3

Remove NA in energy generator:

Show the code
wind_turbines <- wind_turbines |> 
  drop_na(gen_electricity_mw)

# Summary of energy generator (without NA) 
wind_turbines$gen_electricity_mw |> 
  head(10)
 [1] 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3 2.3

calculate the total generation energy capacity.

Show the code
total_capacity_mw <- wind_turbines$gen_electricity_mw |> sum()

print(total_capacity_mw)
[1] 905.3

So the total wind capacity installed in Rogaland is 905.3 MW. Note, that this is only the maximum energy installed and not how much it is generate in the region. It does not take into account the efficiency of the turbines nor wind availability!!.

Exercise 2

Map wind farms and wind turbines from NVE (www.nve.no). The data are free but need to be downloaded from https://nedlasting.nve.no/gis/ before reading into R (save it in a folder: e.g., ~/data/big_data/NVE/NVEData). I have downloaded them in .geojson format. Therefore, we need to read them with the geojsonsf package, which converts GeoJSON to sf objects.

Objetives

  • Load data from local files
  • Data wrangling (preprocessing)
  • Formats of spatial data
  • Spatial intersections (i.e., points - polygons)
  • Types of vector data (i.e., lines, points, polygons)
  • Plot more that one layer in a interactive map

Solution

Show the code
# Libraries
library(geojsonsf)
library(sf)
library(tidyverse)
library(tmap)

Load data from a local file.

  1. Wind turbines (point data)
Show the code
wind_turbines_nve_path <- "data/big_data/NVE/NVEData/Vindkraft_Vindturbin.geojson"
wind_turbines_nve <- geojson_sf(wind_turbines_nve_path) |> 
  # dataUttaksdato to date format
  mutate(dataUttaksdato = ymd(dataUttaksdato))

wind_turbines_nve
Simple feature collection with 272 features and 5 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 4.905257 ymin: 58.31454 xmax: 6.51592 ymax: 59.41634
Geodetic CRS:  WGS 84
First 10 features:
   objektType   saksTittel saksKategori dataUttaksdato              eksportType
1  Vindturbin      Svåheia            2     2024-02-07 NVEs nedlastningsløsning
2  Vindturbin       Tysvær            2     2024-02-07 NVEs nedlastningsløsning
3  Vindturbin    Høg-Jæren            2     2024-02-07 NVEs nedlastningsløsning
4  Vindturbin       Tysvær            2     2024-02-07 NVEs nedlastningsløsning
5  Vindturbin    Bjerkreim            2     2024-02-07 NVEs nedlastningsløsning
6  Vindturbin Vardafjellet            2     2024-02-07 NVEs nedlastningsløsning
7  Vindturbin    Bjerkreim            2     2024-02-07 NVEs nedlastningsløsning
8  Vindturbin   Måkaknuten            2     2024-02-07 NVEs nedlastningsløsning
9  Vindturbin       Utsira            2     2024-02-07 NVEs nedlastningsløsning
10 Vindturbin     Tellenes            2     2024-02-07 NVEs nedlastningsløsning
                    geometry
1  POINT (6.091838 58.38779)
2  POINT (5.552303 59.31521)
3   POINT (5.76918 58.64514)
4   POINT (5.557336 59.2931)
5  POINT (5.934883 58.59081)
6  POINT (5.894319 58.83959)
7  POINT (5.945595 58.58838)
8  POINT (5.966087 58.68458)
9  POINT (4.906018 59.31497)
10 POINT (6.514851 58.34872)
  1. Wind farms areas (Polygons)
Show the code
wind_farms_nve_path <- "data/big_data/NVE/NVEData/Vindkraft_VindkraftanleggOmr.geojson"
wind_farms_nve <- geojson_sf(wind_farms_nve_path) |> 
  # Coherce to dates format
  mutate(across(.cols = ends_with("dato"), .fns = ymd)) |> 
  # Add ID column
   rowid_to_column("id_farm")

wind_farms_nve
Simple feature collection with 57 features and 15 fields
Geometry type: GEOMETRY
Dimension:     XY
Bounding box:  xmin: 4.890143 ymin: 58.2376 xmax: 6.520988 ymax: 59.61306
Geodetic CRS:  WGS 84
First 10 features:
   id_farm dataUttaksdato totaltAntTurbiner    objektType utAvDriftDato
1        1     2024-02-07              <NA> Vindkraftverk          <NA>
2        2     2024-02-07              <NA> Vindkraftverk          <NA>
3        3     2024-02-07              <NA> Vindkraftverk          <NA>
4        4     2024-02-07              <NA> Vindkraftverk          <NA>
5        5     2024-02-07                37 Vindkraftverk          <NA>
6        6     2024-02-07              <NA> Vindkraftverk          <NA>
7        7     2024-02-07              <NA> Vindkraftverk          <NA>
8        8     2024-02-07              <NA> Vindkraftverk          <NA>
9        9     2024-02-07              <NA> Vindkraftverk          <NA>
10      10     2024-02-07              <NA> Vindkraftverk          <NA>
   idriftDato status fylkesnavn
1        <NA>      V   Rogaland
2        <NA>      V   Rogaland
3        <NA>      V   Rogaland
4        <NA>     FJ   Rogaland
5  2019-12-19      D   Rogaland
6        <NA>     FJ   Rogaland
7        <NA>     FJ   Rogaland
8        <NA>      V   Rogaland
9        <NA>      V   Rogaland
10       <NA>     FJ   Rogaland
                                                                    saksLenke
1  https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=194
2   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=29
3   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=36
4   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=52
5   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=32
6   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=37
7   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=64
8   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=55
9   https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=53
10 https://www.nve.no/konsesjon/konsesjonssaker/konsesjonssak?type=A-6&id=120
   forventetProduksjon_Gwh kommunenavn                       geometry
1                     34.0   Stavanger POLYGON ((5.616381 59.07174...
2                    408.0   Bjerkreim POLYGON ((5.892451 58.63726...
3                    785.4   Bjerkreim POLYGON ((5.893369 58.70034...
4                    272.0        Lund POLYGON ((6.26167 58.56276,...
5                    558.4   Bjerkreim POLYGON ((5.995785 58.58672...
6                    306.0   Eigersund POLYGON ((6.206857 58.56671...
7                    306.0     Sokndal POLYGON ((6.153781 58.36661...
8                    544.0     Gjesdal POLYGON ((6.293888 58.82249...
9                    265.2   Bjerkreim POLYGON ((6.07609 58.68459,...
10                   204.0      Karmøy POLYGON ((5.29149 59.23401,...
   saksKategori               tiltakshaver effekt_MW              eksportType
1             2 MARIN ENERGI TESTSENTER AS      10.0 NVEs nedlastningsløsning
2             3         LYSE PRODUKSJON AS     120.0 NVEs nedlastningsløsning
3             3         LYSE PRODUKSJON AS     231.0 NVEs nedlastningsløsning
4             4          BJERKREIM VIND AS      80.0 NVEs nedlastningsløsning
5             2          BJERKREIM VIND AS     159.1 NVEs nedlastningsløsning
6             4          BJERKREIM VIND AS      90.0 NVEs nedlastningsløsning
7             4                  ZEPHYR AS      90.0 NVEs nedlastningsløsning
8             2     GILJA VINDKRAFTVERK AS     160.0 NVEs nedlastningsløsning
9             3                  ZEPHYR AS      78.0 NVEs nedlastningsløsning
10            4      EQUINOR WIND POWER AS      60.0 NVEs nedlastningsløsning

Map both datasets together:

Show the code
tmap_mode("view")


tm_shape(wind_farms_nve) + 
  tm_fill("status", alpha = 0.5, title = "Wind farm status") +
  # Add wind turbines
  tm_shape(wind_turbines_nve) +
  tm_dots(col = "#0072B2")
Figure 3: Wind turbines in Rogaland (Data from NVE)

Note: Status

D - Drift (Operations) N - Nedlagt (Decommissioned) O - Ombygd (Rebuilt) P - Planlagt (Planned) P1 - Planlagt illustrert (Planed illustrated) P2 - Planlagt, prosjektert (Planed, projected) U - Under arbeid (in progress) V - Vedtatt (Adopted) FJ - Fjernet (Removed)

To calculate the actual capacity installed, we need to select only the wind fards that are in operation from wind_farms_nve, and sum the power capacity (effekt_MW). We can do that by montds to see the temporal evolution.

Show the code
power_year <- wind_farms_nve |>
  # Get only farm in operation
  filter(status == "D") |> 
  # Summarize power by month
  group_by(year = lubridate::floor_date(idriftDato, "year")) %>%
  summarize(power_MW = sum(effekt_MW)) |> 
  ungroup() |> 
  # Cummulative sum
  mutate(cumsum_power_MW = cumsum(power_MW))

# Column plot
ggplot(data = power_year,
       aes(x = year, y = cumsum_power_MW)) +
  geom_col(fill = "darkblue") +
  labs(title = "Cumulative wind power installed in Rogaland",
       y = "Power [MW]",
       x = "") +
  theme_bw()

Show the code
"https://nve.geodataonline.no/arcgis/services/Vindkraft2/MapServer/WmsServer?request=GetCapabilities&service=WMS"
[1] "https://nve.geodataonline.no/arcgis/services/Vindkraft2/MapServer/WmsServer?request=GetCapabilities&service=WMS"

We can count now the number of wind turbines per wind farm, to understand for example the volume of blades we may need to recycler.

Show the code
 # Intersect points (wind turbines) wit polygons (wind farms)
number_turbines_farm <- wind_turbines_nve |> 
  # Detect wind farm 
  st_intersection(wind_farms_nve) |> 
  # Number of turbines 
  group_by(id_farm, status,  idriftDato, effekt_MW) |> 
  summarize(n = n()) |> 
  ungroup()

number_turbines_farm
Simple feature collection with 28 features and 5 fields
Geometry type: GEOMETRY
Dimension:     XY
Bounding box:  xmin: 4.905257 ymin: 58.31454 xmax: 6.51592 ymax: 59.41634
Geodetic CRS:  WGS 84
# A tibble: 28 × 6
   id_farm status idriftDato effekt_MW     n                            geometry
     <int> <chr>  <date>         <dbl> <int>                      <GEOMETRY [°]>
 1       2 V      NA              120      1           POINT (5.885113 58.58739)
 2       3 V      NA              231     22 MULTIPOINT ((5.909491 58.66277), (…
 3       5 D      2019-12-19      159.    37 MULTIPOINT ((5.902099 58.58625), (…
 4      14 D      2019-07-25       90     18 MULTIPOINT ((5.871099 58.57491), (…
 5      16 D      2020-06-22       30      7 MULTIPOINT ((5.955698 58.66794), (…
 6      19 D      2020-08-17       30      7 MULTIPOINT ((5.88813 58.8301), (5.…
 7      29 D      2018-02-13       10      3 MULTIPOINT ((5.93595 58.72669), (5…
 8      30 D      2018-10-12       10      2 MULTIPOINT ((5.908438 58.74937), (…
 9      32 P2     NA               15      4 MULTIPOINT ((4.905952 59.31499), (…
10      33 D      2017-11-16      110     33 MULTIPOINT ((6.119956 58.4561), (6…
# ℹ 18 more rows

Now we are going to plot the evolution but in the number od witd turbines in operation:

Show the code
turbines_year <- number_turbines_farm |>
  # Get only farm in operation
  filter(status == "D") |> 
  # Summarize power by month
  group_by(year = lubridate::floor_date(idriftDato, "year")) %>%
  summarize(num_turbines = sum(n)) |> 
  ungroup() |> 
  # Cummulative sum
  mutate(cumsum_num_turbines = cumsum(num_turbines))

# Column plot
ggplot(data = turbines_year,
       aes(x = year, y = cumsum_num_turbines)) +
  geom_col(fill = "darkblue") +
  labs(title = "Cumulative number of wind turbines installed in Rogaland",
       y = "",
       x = "") +
  theme_bw()